#ifndef DYN_PM_FIX_P_H
#define DYN_PM_FIX_P_H
/*
  author Sylvain Bertrand <sylvain.bertrand@gmail.com>
  Protected by linux GNU GPLv2
  Copyright 2012-2014
*/

/* drm refactor */

#define FIXP 32
#define FIXP_ONE (1ULL << FIXP)

static inline s64 s322fixp(s32 a)
{
	return ((s64)a) << FIXP;
}

static inline s32 fixp2s32(u64 a)
{
	return ((s64)a) >> FIXP;
}

static inline u32 fixp_msb_set(s64 a)
{
	u32 shift;
	u32 sign;

	sign = (a >> 63) & 1;
	
	for (shift = 62; shift > 0; --shift)
		if (((a >> shift) & 1) != sign)
			return shift;
	return 0;
}

static inline s64 fixp_mul(s64 a, s64 b)
{
	u32 shift;
	s64 result;

	shift = fixp_msb_set(a) + fixp_msb_set(b);

	if (shift > 61) {
		shift = shift - 61;
		a >>= (shift >> 1) + (shift & 1);
		b >>= shift >> 1;
	} else
		shift = 0;

	result = a * b;

	if (shift > FIXP)
		return result << (shift - FIXP);

	if (shift < FIXP)
		return result >> (FIXP - shift);
	return result;
}

static inline s64 fixp_div(s64 a, s64 b)
{
	u32 shift;
	s64 result;

	shift = 62 - fixp_msb_set(a);
	a <<= shift;

	if (shift < FIXP)
		b >>= (FIXP - shift);

	result = div64_s64(a, b);

	if (shift > FIXP)
		return result >> (shift - FIXP);
	return result;
}

static inline s64 fixp_exp(s64 x)
{
	s64 tolerance;
	s64 sum;
	s64 term;
	s64 y;
	u64 cnt;

	tolerance = div64_s64(FIXP_ONE, 1000000);
	sum = FIXP_ONE;
	y = x;
	cnt = 1;

	if (x < 0)
		y = -1 * x;

	term = y;

	while (term >= tolerance) {
		sum = sum + term;
		cnt = cnt + 1;
		term = fixp_mul(term, div64_s64(y, cnt));
	}

	if (x < 0)
		sum = fixp_div(FIXP_ONE, sum);
	return sum;
}
#endif
